package com.example.leetcode.juc;

import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;

class DiningPhilosophers {

    // 最多允许4个哲学家拿叉子，一定至少有一个哲学家拿到两把叉
    Semaphore semaphore = new Semaphore(4);

    // 五个叉子，就是五个临界资源，对应五把锁
    ReentrantLock[] locks = new ReentrantLock[5];

    public DiningPhilosophers() {
        Arrays.fill(locks, new ReentrantLock());
    }

    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {

        // 获取拿叉子许可证
        semaphore.acquire();

        // 左手叉的编号
        int left = philosopher;
        // 右手叉的编号
        int right = (philosopher - 1 + 5) % 5;

        // 拿左手叉子
        locks[left].lock();
        pickLeftFork.run();

        // 拿右手叉子
        locks[right].lock();
        pickRightFork.run();

        // 吃一次面
        eat.run();

        // 放下右手叉子
        putRightFork.run();
        locks[right].unlock();

        // 放下左手叉子
        putLeftFork.run();
        locks[left].unlock();

        // 抛出许可证（便于给其他人用，重新竞争许可证）
        semaphore.release();

    }
}

public class leetcode1226 {
    public static void main(String[] args) {

        // 需要吃面次数
        int n = 1;

        // 5个哲学家
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        // 哲学家编号 0-4
        DiningPhilosophers diningPhilosophers = new DiningPhilosophers();

        // 开始n轮吃面
        for (int i = 0; i < n; i++) {
            executorService.execute(getTask(diningPhilosophers, 0));
            executorService.execute(getTask(diningPhilosophers, 1));
            executorService.execute(getTask(diningPhilosophers, 2));
            executorService.execute(getTask(diningPhilosophers, 3));
            executorService.execute(getTask(diningPhilosophers, 4));
        }

        // 关闭线程池
        executorService.shutdown();

    }

    /**
     * output[i] = [a, b, c] (3个整数)
     * - a 哲学家编号。
     * - b 指定叉子：{1 : 左边, 2 : 右边}.
     * - c 指定行为：{1 : 拿起, 2 : 放下, 3 : 吃面}。
     *
     * @param diningPhilosophers
     * @param philosopher
     * @return
     */
    private static Runnable getTask(DiningPhilosophers diningPhilosophers, int philosopher) {
        return new Runnable() {
            @Override
            public void run() {
                Runnable pickLeftFork = new Runnable() {
                    @Override
                    public void run() {
                        int[] op = {philosopher, 1, 1};
                        System.out.println(Arrays.toString(op));
                    }
                };
                Runnable pickRightFork = new Runnable() {
                    @Override
                    public void run() {
                        int[] op = {philosopher, 2, 1};
                        System.out.println(Arrays.toString(op));
                    }
                };
                Runnable eat = new Runnable() {
                    @Override
                    public void run() {
                        int[] op = {philosopher, 0, 3};
                        System.out.println(Arrays.toString(op));
                    }
                };
                Runnable putLeftFork = new Runnable() {
                    @Override
                    public void run() {
                        int[] op = {philosopher, 1, 2};
                        System.out.println(Arrays.toString(op));
                    }
                };
                Runnable putRightFork = new Runnable() {
                    @Override
                    public void run() {
                        int[] op = {philosopher, 2, 2};
                        System.out.println(Arrays.toString(op));
                    }
                };
                try {
                    diningPhilosophers.wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }
}
